package arkham.data.util;

import java.util.*;
import arkham.jaxb.encounter.*;
import arkham.util.Deck;
import arkham.util.Pair;

public class EventUtils
{
	/**
	 * Creates an event node that contains a sequence of a certain
	 * type effects, based on the list given. The list contains a pair of
	 * (details,amount) pairs. This is typically used to create a
	 * sequence of "Investigator Gains" effects to distribute items
	 * to investigators.
	 * @param list The list of (details,amount) pairs
	 * @return the event node
	 */
	public static EventNode createTypeSequence(String type,
			List<? extends Pair<?, ?>> list)
	{
		EventNode en = new EventNode();
		for (int i = 0; i < list.size(); i++)
		{
			EventNode node = new EventNode();
			node.setValue(i);
			node.setEffect(createEffect(type, list.get(i).getA().toString(),
					list.get(i).getB().toString()));
			en.getSequence().add(node);
		}
		return en;
	}

	/**
	 * Helper method to create an effect with the specified (type, details, amount)
	 * @param type the type of the effect
	 * @param details the details of the effect
	 * @param amount the amount of the effect
	 * @return the effect type
	 */
	public static EffectType createEffect(String type, String details, String amount) {
		EffectType xmleff = new EffectType();
		xmleff.setType(type);
		xmleff.setDetails(details);
		xmleff.setAmount(amount);
		return xmleff;
	}

	/**
	 * Creates an event node containing a single effect
	 * @param type the type of the effect
	 * @param details the details of the effect
	 * @param amount the amount of the effect
	 * @return the event node
	 */
	public static EventNode createEffectNode(String type, String details, String amount)
	{
		EffectType eff = EventUtils.createEffect(type, details, amount);
		EventNode en = new EventNode();
		en.setEffect(eff);
		return en;
	}

	/**
	 * Creates a Gate Close sequence, which makes a roll, and subsequently
	 * prompts the player on success whether he wishes to spend clues to
	 * seal the gate.
	 * @param gateLoc the location of the gate in Arkham
	 * @param skillUsed the skill used to close the gate
	 * @param modifier the gate close modifier
	 * @param difficulty the gate close difficulty
	 * @param numCluesToSeal the number of clues needed to seal
	 * @return
	 */
	public static EventNode createGateCloseNode(String gateLoc, String skillUsed,
	        int modifier, int difficulty, int numCluesToSeal)
	{
	    EventNode en = new EventNode();
	    en.setRoll(createRollType(skillUsed, modifier, difficulty));

	    EventNode en2 = new EventNode();
	    en2.setChoice(createChoiceType("Do you wish to spend " + numCluesToSeal + " clues " +
	            "to seal the gate?", "Clue", String.valueOf(numCluesToSeal)));
	    EventNode closeEff = EventUtils.createEffectNode("Close Gate", gateLoc, "0");
	    EventNode sealEff = EventUtils.createEffectNode("Close Gate", gateLoc, "1");
	    en2.setChoicepass(sealEff);
	    en2.setChoicefail(closeEff);
	    en.setRollpass(en2);
	    return en;
	}

	/**
	 * Sorts the "sequence" element in an event node
	 * @param node the event node
	 */
	public static void sortSequence(EventNode node)
	{
		Collections.sort(node.getSequence(), valueComparator);
	}

	public static void sortRollCount(EventNode node)
	{
	    Collections.sort(node.getRollcount(), Collections.reverseOrder(valueComparator));
	}

	public static Comparator<EventNode> valueComparator = new Comparator<EventNode>()
	{
		public int compare(EventNode ea, EventNode eb)
		{
			return ea.getValue().compareTo(eb.getValue());
		}
	};

	/**
	 * Creates a string representation of the XML effect type
	 * @param effect the xml effect
	 * @return the string representation
	 */
	public static String printEffect(EffectType effect)
	{
		if (effect.getAmount() == null)
		{
			return effect.getType() + ":" + effect.getDetails();
		}
		else
		{
			return effect.getType() + ":" + effect.getDetails() + "," + effect.getAmount();
		}
	}

	/**
	 * Checks if this mod effect is a Before X check or After X check
	 * kind of mod effect
	 * @param modType the mod effect type
	 * @return true if this mod effect applies to rolls
	 */
	public static boolean isRollingModEffect(String modType)
	{
		if ((modType.startsWith("Before") || modType.startsWith("After")) &&
				modType.endsWith("Check"))
		{
			return true;
		}
		return false;
	}

	/**
	 * Creates a new roll type with the specified type, modifier and difficulty
	 * @param type the type
	 * @param modifier the modifier of the roll
	 * @param difficulty the difficulty
	 * @return the roll type
	 */
	public static RollType createRollType(String type, int modifier, int difficulty)
	{
	    RollType roll = new RollType();
	    roll.setType(type);
	    roll.setModifier(modifier);
	    roll.setDifficulty(difficulty);
	    return roll;
	}

	/**
	 * Creates a choice type with the specified parameters
	 * @param description the question posed to the player
	 * @param cost the cost of the choice, if any. Set to "None" if no cost
	 * @param amount the amount of the cost, if any
	 * @return the choice type
	 */
    public static ChoiceType createChoiceType(String description, String cost, String amount)
    {
        ChoiceType choice = new ChoiceType();
        choice.setDescription(description);
        choice.setCost(cost);
        choice.setAmount(amount);
        return choice;
    }

	/**
	 * Checks if an event state property that is a list of Strings contains a specific
	 * string
	 * @param state the event state to check
	 * @param propName the property name
	 * @param value the value to search for
	 * @return true if the property is defined and the value exists in the list
	 */
	public static boolean listPropertyContains(EventState state, String propName, String value)
	{
	    if (!state.isDefined(propName))
	    {
	        return false;
	    }
	    Object obj = state.getProperty(propName);
	    if (!(obj instanceof List))
	    {
	        return false;
	    }
        List<?> list = (List<?>)obj;
	    return list.contains(value);
	}

	/**
	 * Clears all events in the event stack belonging to a particular player
	 * @param stack the stack to clear from
	 * @param player the index of the player to clear
	 */
	public static void clearEvents(Stack<EventState> stack, int player)
	{
	    Stack<EventState> temp = new Stack<EventState>();
	    while (!stack.isEmpty())
	    {
	        EventState es = stack.pop();
	        if (es.getPlayer() != player)
	        {
	            temp.push(es);
	        }
	    }
	    while (!temp.isEmpty())
	    {
	        stack.push(temp.pop());
	    }
	}

	/**
	 * Gets an event from a deck by its name, but does not discard it
	 * @param deck the deck of event cards
	 * @param name the name of the event
	 * @return the event or null if not found
	 */
	public static EventNode getEventByName(Deck<EventNode> deck, String name)
	{
	    for (EventNode en : deck)
	    {
	        if (en.getName().equals(name))
	        {
	            return en;
	        }
	    }
	    return null;
	}
}
